package gov.pbc.nn.uflo.view;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Component;

import com.bstek.bdf2.core.context.ContextHolder;
import com.bstek.bdf2.core.orm.ParseResult;
import com.bstek.bdf2.uflo.proxy.model.ProxyDefinition;
import com.bstek.bdf2.uflo.proxy.service.ProxyService;
import com.bstek.dorado.annotation.DataProvider;
import com.bstek.dorado.annotation.Expose;
import com.bstek.dorado.core.Configure;
import com.bstek.dorado.data.entity.EntityUtils;
import com.bstek.dorado.data.provider.Criteria;
import com.bstek.dorado.data.provider.Page;
import com.bstek.uflo.model.HistoryTask;
import com.bstek.uflo.model.task.DateUnit;
import com.bstek.uflo.model.task.Task;
import com.bstek.uflo.model.task.TaskState;
import com.bstek.uflo.model.task.TaskType;
import com.bstek.uflo.service.HistoryService;
import com.bstek.uflo.service.ProcessService;
import com.bstek.uflo.service.TaskService;
import com.bstek.uflo.utils.EnvironmentUtils;

import gov.pbc.nn.core.bdf.entity.User;
import gov.pbc.nn.core.dao.PbcnnCoreHibernateDao;
import gov.pbc.nn.core.utils.MapUtils;
import gov.pbc.nn.uflo.dao.AbstractUfloJdbcDao;
import gov.pbc.nn.uflo.entity.Opinion;

@Component(PbcToDoTaskMaintain.BEAN_ID)
public class PbcToDoTaskMaintain extends AbstractUfloJdbcDao {

	public static final String BEAN_ID = "pbc.todoTaskMaintain";
	@Autowired
	@Qualifier("uflo.taskService")
	private TaskService taskService;

	@Autowired
	@Qualifier("uflo.historyService")
	private HistoryService historyService;

	@Autowired
	@Qualifier("uflo.proxy.proxyService")
	private ProxyService proxyService;

	@Resource(name = ProcessService.BEAN_ID)
	private ProcessService processService;

	@Resource(name = PbcnnCoreHibernateDao.BEAN_ID)
	private PbcnnCoreHibernateDao coreHibernateDao;

	@DataProvider
	public String getUserCname(String username) {
		String hql = "select cname from " + User.class.getName() + " where username=:username";
		List<String> list = this.coreHibernateDao.query(hql,
				MapUtils.newHashMap(new String[] { "username" }, new Object[] { username }));
		return (list == null || list.isEmpty()) ? null : list.get(0);
	}

	/**
	 * 加载待办任务
	 * 
	 * @param page
	 * @param criteria
	 */
	@DataProvider
	public void loadTodoTask(Page<Task> page, Criteria criteria) {
		String sql = "SELECT\n" +
				"	m.*\n" +
				"FROM\n" +
				"	(\n" +
				"		SELECT\n" +
				"			t.*, p.NAME_ AS process_name_,u.CNAME_ as promoter_\n" +
				"		FROM\n" +
				"			UFLO_TASK t\n" +
				"		INNER JOIN uflo_process p ON t.PROCESS_ID_ = p.ID_\n" +
				"		INNER JOIN uflo_process_instance pi on pi.PROCESS_ID_=p.ID_ and pi.ID_=t.PROCESS_INSTANCE_ID_\n" +
				"		INNER JOIN t_user u on pi.PROMOTER_=u.USERNAME_\n" +
				"		WHERE\n" +
				"			t.ASSIGNEE_ =?\n" +
				"		ORDER BY\n" +
				"			t.DUEDATE_,\n" +
				"			t.CREATE_DATE_ DESC\n" +
				"	) m\n" +
				"WHERE 1=1 ";
		List<Object> params = new ArrayList<Object>();
		params.add(ContextHolder.getLoginUserName());
		ParseResult result = this.parseCriteria(criteria, false, "m");
		if (result != null) {
			StringBuffer sb = result.getAssemblySql();
			Map<String, Object> valueMap = result.getValueMap();
			params.addAll(valueMap.values());
			sql += " and " + sb.toString();
		}
		this.pagingQuery(page, sql, params.toArray(), new TaskRowMapper());

	}

	/**
	 * 待领取任务
	 * 
	 * @param page
	 * @param criteria
	 */
	@DataProvider
	public void loadParticipatingTask(Page<Task> page, Criteria criteria) {
		String sql = "SELECT\n" +
				"	m.*\n" +
				"FROM\n" +
				"	(\n" +
				"		SELECT\n" +
				"			t.*, p.NAME_ AS process_name_,u.CNAME_ as promoter_\n" +
				"		FROM\n" +
				"			UFLO_TASK t\n" +
				"		INNER JOIN uflo_process p ON t.PROCESS_ID_ = p.ID_\n" +
				"		INNER JOIN UFLO_TASK_PARTICIPATOR tp ON t.ID_ = tp.TASK_ID_\n" +
				"		INNER JOIN uflo_process_instance pi on pi.PROCESS_ID_=p.ID_ and pi.ID_=t.PROCESS_INSTANCE_ID_\n" +
				"		INNER JOIN t_user u on pi.PROMOTER_=u.USERNAME_\n" +
				"		WHERE\n" +
				"			t.STATE_ =?\n" +
				"		AND (tp.USER_ =?) ORDER BY t.DUEDATE_,t.CREATE_DATE_ DESC\n" +
				"	) m\n" +
				"WHERE\n" +
				"	1 = 1";
		List<Object> params = new ArrayList<Object>();
		params.add(TaskState.Ready.name());
		params.add(ContextHolder.getLoginUserName());
		ParseResult result = this.parseCriteria(criteria, false, "m");
		if (result != null) {
			StringBuffer sb = result.getAssemblySql();
			Map<String, Object> valueMap = result.getValueMap();
			params.addAll(valueMap.values());
			sql += " and " + sb.toString();
		}
		this.pagingQuery(page, sql, params.toArray(), new TaskRowMapper());
	}

	/**
	 * 过期任务
	 * 
	 * @param page
	 * @param criteria
	 */
	@DataProvider
	public void loadExpiredTask(Page<Task> page, Criteria criteria) {
		String sql = "SELECT\n" + "	m.*\n" + "FROM\n" + "	(\n" + "		SELECT\n"
				+ "			t.*, p.NAME_ AS process_name_\n" + "		FROM\n" + "			UFLO_TASK t\n"
				+ "		INNER JOIN uflo_process p ON t.PROCESS_ID_ = p.ID_\n" + "		WHERE\n"
				+ "			t.ASSIGNEE_ =?\n" + "		AND t.DUEDATE_ <? ORDER BY t.DUEDATE_,t.CREATE_DATE_ DESC\n"
				+ "	) m\n" + "WHERE\n" + "	1 = 1";
		List<Object> params = new ArrayList<Object>();
		params.add(ContextHolder.getLoginUserName());
		params.add(new Date());
		ParseResult result = this.parseCriteria(criteria, false, "m");
		if (result != null) {
			StringBuffer sb = result.getAssemblySql();
			Map<String, Object> valueMap = result.getValueMap();
			params.addAll(valueMap.values());
			sql += " and " + sb.toString();
		}
		this.pagingQuery(page, sql, params.toArray(), new TaskRowMapper());
	}

	/**
	 * 我处理的任务
	 * 
	 * @param page
	 * @param criteria
	 */
	@DataProvider
	public void loadHistoryTask(Page<HistoryTask> page, Criteria criteria) {
		String sql = "SELECT\n" +
				"	m.*\n" +
				"FROM\n" +
				"	(\n" +
				"		SELECT\n" +
				"			t.*, p.NAME_ AS process_name_,u.CNAME_ as promoter_\n" +
				"		FROM\n" +
				"			UFLO_HIS_TASK t\n" +
				"		INNER JOIN uflo_process p ON t.PROCESS_ID_ = p.ID_\n" +
				"		INNER JOIN uflo_his_process_instance pi on pi.PROCESS_ID_=p.ID_ and pi.PROCESS_INSTANCE_ID_=t.PROCESS_INSTANCE_ID_\n" +
				"		INNER JOIN t_user u on pi.PROMOTER_=u.USERNAME_\n" +
				"		WHERE\n" +
				"			t.ASSIGNEE_ =?\n" +
				"		ORDER BY\n" +
				"			t.CREATE_DATE_ DESC\n" +
				"	) m\n" +
				"WHERE\n" +
				"	1 = 1";

		List<Object> params = new ArrayList<Object>();
		params.add(ContextHolder.getLoginUserName());
		ParseResult result = this.parseCriteria(criteria, false, "m");
		if (result != null) {
			StringBuffer sb = result.getAssemblySql();
			Map<String, Object> valueMap = result.getValueMap();
			params.addAll(valueMap.values());
			sql += " and " + sb.toString();
		}
		this.pagingQuery(page, sql, params.toArray(), new HisTaskRowMapper());
	}

	/**
	 * 获取代理的待办任务
	 * 
	 * @param page
	 * @param criteria
	 * @param params
	 */
	@DataProvider
	public void loadProxyTodoTasks(Page<Task> page, Criteria criteria, Map<String, Object> params) {
		String issueUser = (String) params.get("issueUser");
		Date start = (Date) params.get("start");
		Date end = (Date) params.get("end");
		boolean isProxyAll = ((Boolean) params.get("isProxyAll")).booleanValue();
		String proxyDefId = params.get("proxyDefId").toString();

		List<Object> ps = new ArrayList<Object>();
		ps.add(issueUser);
		ps.add(start);
		ps.add(end);
		List<Long> proxyDefList = Collections.emptyList();
		if (!isProxyAll) {
			proxyDefList = this.proxyService.findProxyProcessList(proxyDefId);
			if (proxyDefList != null && !proxyDefList.isEmpty())
				ps.add(proxyDefList);
		}
		if ((proxyDefList.size() > 0) || (isProxyAll)) {
			String sql = "SELECT\n" +
					"	m.*\n" +
					"FROM\n" +
					"	(\n" +
					"		SELECT\n" +
					"			t.*, p.NAME_ AS process_name_,u.CNAME_ as promoter_\n" +
					"		FROM\n" +
					"			uflo_task t\n" +
					"		INNER JOIN uflo_process p ON t.PROCESS_ID_ = p.ID_\n" +
					"		inner join uflo_process_instance pi on pi.PROCESS_ID_=p.ID_ and pi.ID_=t.PROCESS_INSTANCE_ID_\n" +
					"		INNER JOIN t_user u on pi.PROMOTER_=u.USERNAME_\n" +
					"		WHERE\n" +
					"			t.ASSIGNEE_ =?\n" +
					"		AND t.CREATE_DATE_ >=?\n" +
					"		AND t.CREATE_DATE_ <=?\n" +
					"		ORDER BY\n" +
					"			t.DUEDATE_,\n" +
					"			t.CREATE_DATE_ DESC\n" +
					"	) m\n" +
					"WHERE\n" +
					"	1 = 1";

			ParseResult result = this.parseCriteria(criteria, false, "m");
			if (result != null) {
				StringBuffer sb = result.getAssemblySql();
				Map<String, Object> valueMap = result.getValueMap();
				ps.addAll(valueMap.values());
				sql += " and " + sb.toString();
			}
			this.pagingQuery(page, sql, ps.toArray(), new TaskRowMapper());
		}
	}

	/**
	 * 获取代理的待领取的任务
	 * 
	 * @param page
	 * @param criteria
	 * @param params
	 */
	@DataProvider
	public void loadProxyPartiTasks(Page<Task> page, Criteria criteria, Map<String, Object> params) {
		String issueUser = (String) params.get("issueUser");
		Date start = (Date) params.get("start");
		Date end = (Date) params.get("end");
		boolean isProxyAll = ((Boolean) params.get("isProxyAll")).booleanValue();
		String proxyDefId = params.get("proxyDefId").toString();

		List<Object> ps = new ArrayList<Object>();
		ps.add(issueUser);
		ps.add(TaskState.Ready.name());
		ps.add(start);
		ps.add(end);
		List<Long> proxyDefList = Collections.emptyList();
		if (!isProxyAll) {
			proxyDefList = this.proxyService.findProxyProcessList(proxyDefId);
			if (proxyDefList != null && !proxyDefList.isEmpty())
				ps.add(proxyDefList);
		}
		if ((proxyDefList.size() > 0) || (isProxyAll)) {
			String sql = "SELECT\n" +
					"	m.*\n" +
					"FROM\n" +
					"	(\n" +
					"		SELECT\n" +
					"			t.*, p.NAME_ AS process_name_,u.CNAME_ as promoter_\n" +
					"		FROM\n" +
					"			uflo_task t\n" +
					"		INNER JOIN uflo_process p ON t.PROCESS_ID_ = p.ID_\n" +
					"		INNER JOIN UFLO_TASK_PARTICIPATOR tp ON t.ID_ = tp.TASK_ID_\n" +
					"		INNER JOIN uflo_process_instance pi on pi.PROCESS_ID_=p.ID_ and pi.ID_=t.PROCESS_INSTANCE_ID_\n" +
					"		INNER JOIN t_user u on pi.PROMOTER_=u.USERNAME_\n" +
					"		WHERE\n" +
					"			tp.USER_ =?\n" +
					"		AND t.STATE_ =?\n" +
					"		AND t.CREATE_DATE_ >=?\n" +
					"		AND t.CREATE_DATE_ <=?\n" +
					"		ORDER BY\n" +
					"			t.DUEDATE_,\n" +
					"			t.CREATE_DATE_ DESC\n" +
					"	) m\n" +
					"WHERE\n" +
					"	1 = 1";

			ParseResult result = this.parseCriteria(criteria, false, "m");
			if (result != null) {
				StringBuffer sb = result.getAssemblySql();
				Map<String, Object> valueMap = result.getValueMap();
				ps.addAll(valueMap.values());
				sql += " and " + sb.toString();
			}
			this.pagingQuery(page, sql, ps.toArray(), new TaskRowMapper());
		}
	}

	/**
	 * 获取代理定义
	 * 
	 * @return
	 */
	@DataProvider
	public List<ProxyDefinition> loadProxyDefinitions() {
		return this.proxyService.findProxyDefinitions(EnvironmentUtils.getEnvironment().getLoginUser());
	}

	@Expose
	public Long getTaskAlarmTime() {
		Long millisecondsBeforeDueDateToRemind = Long
				.valueOf(Configure.getLong("uflo.minutesBeforeDueDateToRemind") * 60L * 1000L);
		return Long.valueOf(System.currentTimeMillis() + millisecondsBeforeDueDateToRemind.longValue());
	}

	/**
	 * 领取任务
	 * 
	 * @param taskId
	 */
	@Expose
	public void claimTask(long taskId) {
		this.taskService.claim(taskId, EnvironmentUtils.getEnvironment().getLoginUser());
	}

	/**
	 * 释放任务
	 * 
	 * @param taskId
	 */
	@Expose
	public void releaseTask(long taskId) {
		this.taskService.release(taskId);
	}

	@Expose
	public void complete(Task task) {
		this.taskService.start(task.getId());
		this.taskService.complete(task.getId());
	}

	/**
	 * 领取代理任务
	 * 
	 * @param taskId
	 * @param user
	 */
	@Expose
	public void proxyClaimTask(long taskId, String user) {
		this.taskService.claim(taskId, user);
	}

	/**
	 * 加载审核意见
	 * 
	 * @param page
	 * @param params
	 */
	@DataProvider
	public void loadTaskOpinion(Page<Opinion> page, Map<String, Object> params) {
		String businessId = (String) params.get("businessId");
		String sql = "select * from t_uflo_opinion o where o.BUS_KEY_=? and o.INVALID_=0 ORDER BY o.TASK_ID_ desc, o.CREATE_DATE_ desc";
		this.pagingQuery(page, sql, new Object[] { businessId }, new RowMapper<Opinion>() {

			@Override
			public Opinion mapRow(ResultSet rs, int rowNum) throws SQLException {
				Opinion o = new Opinion();
				o.setId(rs.getString("ID_"));
				o.setBusKey(rs.getString("BUS_KEY_"));
				o.setBusTable(rs.getString("BUS_TABLE_"));
				o.setCname(rs.getString("CNAME_"));
				o.setDeptId(rs.getString("DEPT_ID_"));
				o.setDeptName(rs.getString("DEPT_NAME_"));
				o.setInvalid(rs.getInt("INVALID_") == 0);
				o.setOpinion(rs.getString("OPINION_"));
				o.setProcessName(rs.getString("PROCESS_NAME_"));
				o.setTaskId(rs.getLong("TASK_ID_"));
				o.setTaskName(rs.getString("TASK_NAME_"));
				o.setUsername(rs.getString("USERNAME_"));
				o.setUpdateDate(rs.getTimestamp("UPDATE_DATE_"));
				o.setUpdateUser(rs.getString("UPDATE_USER_"));
				o.setCreateDate(rs.getTimestamp("CREATE_DATE_"));
				o.setCreateUser(rs.getString("CREATE_USER_"));

				try {
					o = EntityUtils.toEntity(o);
					byte[] sign = rs.getBytes("OPINION_IMG_");
					if (sign != null && sign.length > 0) {
						EntityUtils.setValue(o, "_sign", 1);
						String signStr = new String(sign);
						if (StringUtils.startsWith(signStr, "data:image")) {
							signStr = StringUtils.substring(signStr, StringUtils.indexOf(signStr, ",") + 1);
							o.setOpinion(signStr);
						} else {
							String base64 = Base64.encodeBase64String(sign);
							o.setOpinion(base64);
						}
					}
				} catch (Exception e) {
					e.printStackTrace();
				}

				return o;
			}

		});
	}

	public static class TaskRowMapper implements RowMapper<Task> {

		@Override
		public Task mapRow(ResultSet rs, int rowNum) throws SQLException {
			Task task = new Task();
			task.setId(rs.getLong("ID_"));
			task.setAssignee(rs.getString("ASSIGNEE_"));
			task.setBusinessId(rs.getString("BUSINESS_ID_"));
			task.setCountersignCount(rs.getInt("COUNTERSIGN_COUNT_"));
			task.setCreateDate(rs.getTimestamp("CREATE_DATE_"));
			if (StringUtils.isNotBlank(rs.getString("DATE_UNIT_"))) {
				task.setDateUnit(DateUnit.valueOf(rs.getString("DATE_UNIT_")));
			}
			task.setDescription(rs.getString("DESCRIPTION_"));
			task.setDueActionDate(rs.getTimestamp("DUE_ACTION_DATE_"));
			task.setDuedate(rs.getTimestamp("DUEDATE_"));
			task.setNodeName(rs.getString("NODE_NAME_"));
			task.setOpinion(rs.getString("OPINION_"));
			task.setOwner(rs.getString("OWNER_"));
			if (StringUtils.isNotBlank(rs.getString("PREV_STATE_"))) {
				task.setPrevState(TaskState.valueOf(rs.getString("PREV_STATE_")));
			}
			task.setPrevTask(rs.getString("PREV_TASK_"));
			task.setProcessId(rs.getLong("PROCESS_ID_"));
			task.setProcessInstanceId(rs.getLong("PROCESS_INSTANCE_ID_"));
			task.setProgress(rs.getInt("PROGRESS_"));
			task.setRootProcessInstanceId(rs.getLong("ROOT_PROCESS_INSTANCE_ID_"));
			if (StringUtils.isNotBlank(rs.getString("STATE_"))) {
				task.setState(TaskState.valueOf(rs.getString("STATE_")));
			}
			task.setTaskName(rs.getString("TASK_NAME_"));
			if (StringUtils.isNotBlank(rs.getString("TYPE_"))) {
				task.setType(TaskType.valueOf(rs.getString("TYPE_")));
			}
			task.setUrl(rs.getString("URL_"));
			try {
				task = EntityUtils.toEntity(task);
			} catch (Exception e) {
				e.printStackTrace();
			}
			try {
				EntityUtils.setValue(task, "processName", rs.getString("process_name_"));
			} catch (Exception e) {
				e.printStackTrace();
			}
			try {
				EntityUtils.setValue(task, "promoter", rs.getString("promoter_"));
			} catch (Exception e) {
				e.printStackTrace();
			}
			return task;
		}

	}

	public static class HisTaskRowMapper implements RowMapper<HistoryTask> {

		@Override
		public HistoryTask mapRow(ResultSet rs, int rowNum) throws SQLException {
			HistoryTask task = new HistoryTask();
			task.setId(rs.getLong("ID_"));
			task.setAssignee(rs.getString("ASSIGNEE_"));
			task.setBusinessId(rs.getString("BUSINESS_ID_"));
			task.setCreateDate(rs.getTimestamp("CREATE_DATE_"));
			task.setDescription(rs.getString("DESCRIPTION_"));
			task.setDuedate(rs.getTimestamp("DUEDATE_"));
			task.setNodeName(rs.getString("NODE_NAME_"));
			task.setOpinion(rs.getString("OPINION_"));
			task.setOwner(rs.getString("OWNER_"));
			task.setProcessId(rs.getLong("PROCESS_ID_"));
			task.setProcessInstanceId(rs.getLong("PROCESS_INSTANCE_ID_"));
			task.setRootProcessInstanceId(rs.getLong("ROOT_PROCESS_INSTANCE_ID_"));
			task.setTaskId(rs.getLong("TASK_ID_"));
			if (StringUtils.isNotBlank(rs.getString("STATE_"))) {
				task.setState(TaskState.valueOf(rs.getString("STATE_")));
			}
			task.setTaskName(rs.getString("TASK_NAME_"));
			if (StringUtils.isNotBlank(rs.getString("TYPE_"))) {
				task.setType(TaskType.valueOf(rs.getString("TYPE_")));
			}
			task.setUrl(rs.getString("URL_"));
			try {
				task = EntityUtils.toEntity(task);
			} catch (Exception e) {
				e.printStackTrace();
			}
			try {
				EntityUtils.setValue(task, "processName", rs.getString("process_name_"));
			} catch (Exception e) {
				e.printStackTrace();
			}
			try {
				EntityUtils.setValue(task, "promoter", rs.getString("promoter_"));
			} catch (Exception e) {
				e.printStackTrace();
			}
			return task;
		}
	}
}
